<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="cdEVC" id="cdEVC"><span data-lake-id="u58467a41" id="u58467a41">典型回答</span></h1>
  <p data-lake-id="u9f816ffd" id="u9f816ffd"><br></p>
  <p data-lake-id="u4e19a3e6" id="u4e19a3e6"><span data-lake-id="u310bd430" id="u310bd430">微服务之间的循环依赖是指在微服务架构中，两个或多个服务相互依赖对方的情况。这里的依赖其实就是互相调用。两个微服务互相调用，A调用B，B又反向调用A，或者A调用B，B调用C，C调用A，这都是循环依赖。</span></p>
  <p data-lake-id="uae9144db" id="uae9144db"><span data-lake-id="u3e650a47" id="u3e650a47">​</span><br></p>
  <p data-lake-id="uf6b0660a" id="uf6b0660a"><span data-lake-id="u0f067250" id="u0f067250">举个例子，假设我们有两个微服务：订单服务（Order Service）和库存服务（Inventory Service）。</span></p>
  <ol list="ub9a055bd">
   <li fid="ub146d137" data-lake-id="u2b969945" id="u2b969945"><span data-lake-id="ue1da6404" id="ue1da6404">订单服务（Order Service）</span><span data-lake-id="u578f261c" id="u578f261c">：</span></li>
  </ol>
  <ul list="u39f7ea3d" data-lake-indent="1">
   <li fid="u4174f99d" data-lake-id="u7f0b36c8" id="u7f0b36c8"><span data-lake-id="u2ff326bd" id="u2ff326bd">当用户下单时，订单服务需要调用库存服务来检查所需商品的库存。</span></li>
   <li fid="u4174f99d" data-lake-id="u2cfb7a2e" id="u2cfb7a2e"><span data-lake-id="ua99ee580" id="ua99ee580">如果库存充足，订单服务会继续处理订单。</span></li>
  </ul>
  <ol list="ub9a055bd" start="2">
   <li fid="ub146d137" data-lake-id="u6f13de70" id="u6f13de70"><span data-lake-id="uaad1fa7d" id="uaad1fa7d">库存服务（Inventory Service）</span><span data-lake-id="u3980f765" id="u3980f765">：</span></li>
  </ol>
  <ul list="uae72f2e0" data-lake-indent="1">
   <li fid="ua9d3124c" data-lake-id="uf7cf41c0" id="uf7cf41c0"><span data-lake-id="udab5bf0a" id="udab5bf0a">当库存服务更新库存时，它需要通知订单服务更新订单状态。</span></li>
   <li fid="ua9d3124c" data-lake-id="uebdf7fd7" id="uebdf7fd7"><span data-lake-id="udf04dfcd" id="udf04dfcd">为此，库存服务会调用订单服务的一个接口。</span></li>
  </ul>
  <p data-lake-id="u9c4bb1cf" id="u9c4bb1cf"><span data-lake-id="u809120cb" id="u809120cb">这里的循环依赖出现在：</span></p>
  <ul list="u40e35bc0">
   <li fid="uc0756f23" data-lake-id="u802d3865" id="u802d3865"><span data-lake-id="ud8c9fbce" id="ud8c9fbce">订单服务依赖库存服务来验证库存。</span></li>
   <li fid="uc0756f23" data-lake-id="ua0baef8d" id="ua0baef8d"><span data-lake-id="u87244c50" id="u87244c50">库存服务依赖订单服务来更新订单状态。</span></li>
  </ul>
  <p data-lake-id="u6ff3186f" id="u6ff3186f"><span data-lake-id="u363d266e" id="u363d266e">​</span><br></p>
  <p data-lake-id="u5c01b531" id="u5c01b531"><span data-lake-id="u0ed124d1" id="u0ed124d1">我们在做设计的时候应该尽量的避免循环依赖，因为循环依赖会导致以下这些问题：</span></p>
  <p data-lake-id="uba6d4a4a" id="uba6d4a4a"><span data-lake-id="u1f32bd50" id="u1f32bd50">​</span><br></p>
  <p data-lake-id="ud7bea788" id="ud7bea788"><strong><span data-lake-id="u4702206c" id="u4702206c">1、流量放大</span></strong><span data-lake-id="u7c704c2a" id="u7c704c2a">：因为系统之间存在循环依赖，那么就会导致本来下单系统可能只有100 QPS，但是因为存在循环依赖，就会导致这个QPS被放大，因为100个请求调用到订单服务，订单服务就有100个请求调到库存服务，而库存服务又有100个请求再调到订单服务。就导致订单服务有200的QPS了。无形中被放大了流量。</span></p>
  <p data-lake-id="u7f0cc285" id="u7f0cc285"><span data-lake-id="u67d99cce" id="u67d99cce">​</span><br></p>
  <p data-lake-id="u6ff81be1" id="u6ff81be1"><strong><span data-lake-id="ub81a36eb" id="ub81a36eb">2、性能问题</span></strong><span data-lake-id="ueb4caa4d" id="ueb4caa4d">：因为存在循环依赖，那么服务之间需要等待彼此的响应，就会无形中拖长请求的RT，让接口性能变的更慢。</span></p>
  <p data-lake-id="u65475f66" id="u65475f66"><span data-lake-id="uc326e88e" id="uc326e88e">​</span><br></p>
  <p data-lake-id="u49c92494" id="u49c92494"><strong><span data-lake-id="uf4fb8fd1" id="uf4fb8fd1">3、互相影响</span></strong><span data-lake-id="u862005ed" id="u862005ed">：如果一个服务出现问题，这个问题可能会通过循环依赖影响到另一个服务。而一个服务中的错误可能通过依赖链传播到其他服务，增加了系统出现级联故障的风险。</span></p>
  <p data-lake-id="u1a550dc3" id="u1a550dc3"><span data-lake-id="u41e259e3" id="u41e259e3">​</span><br></p>
  <p data-lake-id="ub4efb51a" id="ub4efb51a"><strong><span data-lake-id="u5402467b" id="u5402467b">4、发布困难</span></strong><span data-lake-id="ua53320e9" id="ua53320e9">：每当一个服务需要更新时，我们需要同时考虑他依赖了谁以及谁依赖了他。一般是被依赖的应用先发布。但是因为系统间存在循环依赖，那么在上一个新的功能的时候需要发布时，就会带来很大的问题，那就是谁先发的问题，而谁先发都会有问题。</span></p>
  <p data-lake-id="u19e523d9" id="u19e523d9"><br></p>
  <p data-lake-id="u4946ff0c" id="u4946ff0c"><span data-lake-id="uc7270ef1" id="uc7270ef1">主要就是以上这几个问题，所以我们平时需要尽量的避免循环依赖。</span></p>
  <p data-lake-id="uba472b0d" id="uba472b0d"><span data-lake-id="u46922642" id="u46922642">​</span><br></p>
  <h1 data-lake-id="ilWZQ" id="ilWZQ"><span data-lake-id="uf90e4e7f" id="uf90e4e7f">扩展知识</span></h1>
  <p data-lake-id="u6d01107b" id="u6d01107b"><br></p>
  <h2 data-lake-id="bp0wv" id="bp0wv"><span data-lake-id="u1b79f820" id="u1b79f820">如何解决？</span></h2>
  <p data-lake-id="u88ccae63" id="u88ccae63"><br></p>
  <p data-lake-id="u6a9f0964" id="u6a9f0964"><span data-lake-id="u69cdff59" id="u69cdff59">首先，遇到循环依赖的问题，第一件事就是考虑设计的不合理性，一般来说系统会有自己的明确的职责，并且一张架构图中，一个系统一定是有属于他自己的位置的，一个系统又在上游，又在下游，那一定是设计的不合理，所以需要考虑做</span><strong><span data-lake-id="u568f4638" id="u568f4638" style="color: #DF2A3F">重新设计</span></strong><span data-lake-id="u738d62d3" id="u738d62d3">。</span></p>
  <p data-lake-id="u54e9ee8c" id="u54e9ee8c"><span data-lake-id="u3be92a79" id="u3be92a79">​</span><br></p>
  <p data-lake-id="uca350d6c" id="uca350d6c"><span data-lake-id="ua82e2f08" id="ua82e2f08">其次，像前面我们提到的例子，库存需要通知订单更新状态，这个过程我们可以把服务调用改成</span><strong><span data-lake-id="u60684fb6" id="u60684fb6" style="color: #DF2A3F">消息通信</span></strong><span data-lake-id="u2061c5db" id="u2061c5db">，通过异步消息来解耦调两个系统之间的相互依赖。这样就可以避免互相影响。</span></p>
  <p data-lake-id="u59cf4833" id="u59cf4833"><span data-lake-id="u51d051da" id="u51d051da">​</span><br></p>
  <p data-lake-id="u844fe39c" id="u844fe39c"><span data-lake-id="ue93c4b0b" id="ue93c4b0b">另外，比较常用的一种方式，那就是引入一个</span><strong><span data-lake-id="u6f10d135" id="u6f10d135" style="color: #DF2A3F">共享库</span></strong><span data-lake-id="u0f31e16c" id="u0f31e16c">，当出现循环依赖时候，可以考虑将共享部分抽取出来作为一个共享库，然后由各个相关服务共同引用这个库。通过在循环依赖的两个微服务中引用共享库，而不是直接调用对方的接口来操作数据。</span></p>
  <p data-lake-id="u3bcbe746" id="u3bcbe746"><span data-lake-id="u769a18e8" id="u769a18e8">​</span><br></p>
  <p data-lake-id="u471c37b9" id="u471c37b9"><span data-lake-id="u11d9aca9" id="u11d9aca9">上面的共享库，也可以做成一个</span><strong><span data-lake-id="uadc93a11" id="uadc93a11" style="color: #DF2A3F">共享服务（或者叫中介服务）</span></strong><span data-lake-id="u66241d69" id="u66241d69">，你俩不是互相依赖么，那么干脆我单独搞一个服务出来，你们都直接依赖他而不是做互相依赖。比如我们实际业务中就有一个台账系统，我们会把各个业务流水都写到台账中，所以当我们上游系统之间需要查询数据的时候，就可以直接去台账查，而不需要依赖其他的系统。而台账作为最下游系统，他也不会调用任何其他服务，他最多会提供消息给别的系统。</span></p>
  <p data-lake-id="u407ed6f7" id="u407ed6f7"><br></p>
  <h1 data-lake-id="y2Y09" id="y2Y09"><span data-lake-id="uf70cf849" id="uf70cf849">扩展知识</span></h1>
  <p data-lake-id="u36e17a44" id="u36e17a44"><br></p>
  <h2 data-lake-id="XozQl" id="XozQl"><span data-lake-id="u917bcbae" id="u917bcbae">如何识别循环依赖</span></h2>
  <p data-lake-id="ua81e3b74" id="ua81e3b74"><br></p>
  <p data-lake-id="u246fcbf0" id="u246fcbf0"><span data-lake-id="u03cfc5fc" id="u03cfc5fc">要解决循环依赖，首先需要识别出来。那么具体如何识别呢？</span></p>
  <p data-lake-id="uf179448e" id="uf179448e"><span data-lake-id="u7bc250f8" id="u7bc250f8">​</span><br></p>
  <p data-lake-id="u770667fe" id="u770667fe"><span data-lake-id="u9337da14" id="u9337da14">首先，一个一劳永逸的方式，就是有一个</span><strong><span data-lake-id="u36fb06dc" id="u36fb06dc">明确地架构图</span></strong><span data-lake-id="uaebfc523" id="uaebfc523">，架构图中明确的标注出来各个系统处于什么位置，上游系统都有谁，下游系统都有谁，依赖只能是从上到下的，而不能是从下到上的。有了这样一张图，大家就都知道，我自己的系统处于什么位置，我可以依赖谁，谁可以依赖我了。</span></p>
  <p data-lake-id="u8456f1c2" id="u8456f1c2"><span data-lake-id="u85790287" id="u85790287">​</span><br></p>
  <p data-lake-id="u5eada41e" id="u5eada41e"><span data-lake-id="u844e83db" id="u844e83db">其实就是可以通过</span><strong><span data-lake-id="u5b7f8825" id="u5b7f8825">链路追踪</span></strong><span data-lake-id="u18b1a2ac" id="u18b1a2ac">来看是否存在循环依赖，当一个调用的trace中，一个应用被调用多次，那么可能就会存在循环依赖的问题。</span></p>
  <p data-lake-id="ub5182b9c" id="ub5182b9c"><span data-lake-id="u7f4bad79" id="u7f4bad79">​</span><br></p>
  <p data-lake-id="u85425aa1" id="u85425aa1"><br></p>
  <p data-lake-id="u081de1ec" id="u081de1ec"><span data-lake-id="uf328549e" id="uf328549e">还有就是通过各种</span><strong><span data-lake-id="uf30a5558" id="uf30a5558">评审</span></strong><span data-lake-id="uc719025b" id="uc719025b">来发现了，比如我们前面提到的，循环依赖会导致发布过程存在谁先发的问题，这个问题在发布评审时是一定可以暴露出来的。</span></p>
  <p data-lake-id="ua16c18b6" id="ua16c18b6"><span data-lake-id="u82076e0e" id="u82076e0e">​</span><br></p>
  <p data-lake-id="u21308960" id="u21308960"><span data-lake-id="udc86f3d2" id="udc86f3d2">所以，在设计评审，发布评审，TC评审，以及代码评审的时候，需要关注一下是否存在着循环依赖的问题。</span></p>
  <p data-lake-id="u2b9215bb" id="u2b9215bb"><span data-lake-id="u94ab69e9" id="u94ab69e9">​</span><br></p>
  <p data-lake-id="uba509f0d" id="uba509f0d"><span data-lake-id="ua9386fd0" id="ua9386fd0">​</span><br></p>
 </body>
</html>